﻿#region LICENSE
/****************************************************************************
 *                                                                          *
 *  CoderBlu.Collections - Thread-safe Collections                          *
 *  Copyright (C) 2008 Rodger "Nullz" Aiken <nullz.void@gmail.com>          *
 *                                                                          *
 *  This library is free software; you can redistribute it and/or modify    *
 *  it under the terms of the GNU Library General Public License as         *
 *  published by the Free Software Foundation; either version 2 of the      *
 *  License, or (at your option) any later version.                         *
 *                                                                          *
 *  This library is distributed in the hope that it will be useful, but     *
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT     *
 *  ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library        *
 *  General Public License for more details.                                *
 *                                                                          *
 *  You should have received a copy of the GNU Library General Public       *
 *  License along with this library; if not, write to the Free Foundation,  *
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA        *
 *                                                                          *
 ****************************************************************************/
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace CoderBlu.Collections
{
	[Serializable]
	public class List : List<object>
	{
		public List ( )
			: base() {
		}
		public List ( int capacity )
			: base(capacity) {
		}
		public List ( IEnumerable collection )
			: base() {
			if ( collection == null )
				throw new ArgumentNullException("collection");

			foreach ( object item in collection )
				this.Add(item);
		}
		public List ( IEnumerable<object> collection )
			: base(collection) {
		}

		protected List ( SerializationInfo info, StreamingContext context )
			: base(info, context) {
		}
	}

	[Serializable]
	public class List<T> :
		SynchronizedCollection,
		ICollection, ICollection<T>,
		IEnumerable, IEnumerable<T>,
		IList, IList<T>,
		ISerializable, IDeserializationCallback
	{
		private SerializationInfo _info;
		private T [ ] _items;
		private volatile int _count;

		public List ( )
			: this(0) {
		}
		public List ( int capacity ) {
			if ( capacity < 0 )
				throw new ArgumentOutOfRangeException("capacity");

			this._items = new T [ capacity ];
		}
		public List ( IEnumerable<T> collection )
			: this() {
			if ( collection == null )
				throw new ArgumentNullException("collection");

			foreach ( T item in collection )
				this.Add(item);
		}

		protected List ( SerializationInfo info, StreamingContext context ) {
			this._info = info;
		}

		public int Count {
			get { return this._count; }
		}

		public T this [ int index ] {
			get {
				if ( ( index < 0 ) || ( index >= this._count ) )
					throw new ArgumentOutOfRangeException("index");

				lock ( this._synclock )
					return this._items [ index ];
			}
			set {
				if ( ( index < 0 ) || ( index >= this._count ) )
					throw new ArgumentOutOfRangeException("index");

				lock ( this._synclock )
					this._items [ index ] = value;
			}
		}

		bool ICollection.IsSynchronized {
			get { return true; }
		}

		object ICollection.SyncRoot {
			get { return this; }
		}

		bool ICollection<T>.IsReadOnly {
			get { return false; }
		}

		object IList.this [ int index ] {
			get { return this [ index ]; }
			set { this [ index ] = ( ( T ) value ); }
		}

		bool IList.IsFixedSize {
			get { return false; }
		}

		bool IList.IsReadOnly {
			get { return false; }
		}

		public void Add ( T item ) {
			lock ( this._synclock ) {
				if ( ( this._count + 1 ) > this._items.Length )
					Array.Resize<T>(ref this._items, ( ( this._items.Length << 1 ) | 1 ));

				this._items [ this._count++ ] = item;
			}
		}

		public void AddRange ( IEnumerable<T> collection ) {
			if ( collection == null )
				throw new ArgumentNullException("collection");

			foreach ( T item in collection )
				this.Add(item);
		}

		public int BinarySearch ( T value ) {
			return this.BinarySearch(value, Comparer<T>.Default);
		}
		public int BinarySearch ( T value, IComparer<T> comparer ) {
			return this.BinarySearch(0, this._count, value, comparer);
		}
		public int BinarySearch ( int offset, int count, T value ) {
			return this.BinarySearch(offset, count, value, Comparer<T>.Default);
		}
		public int BinarySearch ( int offset, int count, T value, IComparer<T> comparer ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( comparer == null )
				throw new ArgumentNullException("comparer");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					return Array.BinarySearch<T>(this._items, offset, count, value, comparer);
			}

			return ( -1 );
		}

		public void Clear ( ) {
			if ( this._count > 0 ) {
				lock ( this._synclock )
					Array.Clear(this._items, 0, this._count);
			}
		}

		public bool Contains ( T item ) {
			if ( this._count > 0 ) {
				lock ( this._synclock )
					return ( this.IndexOf(item) >= 0 );
			}

			return false;
		}

		public List<TOutput> ConvertAll<TOutput> ( Converter<T, TOutput> converter ) {
			return this.ConvertAll<TOutput>(0, converter);
		}
		public List<TOutput> ConvertAll<TOutput> ( int offset, Converter<T, TOutput> converter ) {
			return this.ConvertAll<TOutput>(offset, ( this._count - offset ), converter);
		}
		public List<TOutput> ConvertAll<TOutput> ( int offset, int count, Converter<T, TOutput> converter ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( converter == null )
				throw new ArgumentNullException("converter");

			List<TOutput> list = new List<TOutput>();

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = offset ; i < ( offset + count ) ; i++ )
						list.Add(converter(this._items [ i ]));
				}
			}

			return list;
		}

		public void CopyTo ( T [ ] dstArray ) {
			this.CopyTo(dstArray, 0);
		}
		public void CopyTo ( T [ ] dstArray, int dstIndex ) {
			this.CopyTo(dstArray, dstIndex, ( dstArray.Length - dstIndex ));
		}
		public void CopyTo ( T [ ] dstArray, int dstIndex, int count ) {
			this.CopyTo(0, dstArray, dstIndex, count);
		}
		public void CopyTo ( int srcIndex, T [ ] dstArray, int dstIndex, int count ) {
			if ( ( srcIndex < 0 ) || ( srcIndex >= this._count ) )
				throw new ArgumentOutOfRangeException("srcIndex");

			if ( dstArray == null )
				throw new ArgumentNullException("dstArray");

			if ( ( dstIndex < 0 ) || ( dstIndex >= dstArray.Length ) )
				throw new ArgumentOutOfRangeException("dstIndex");

			if ( count < 0 )
				throw new ArgumentOutOfRangeException("count");

			if ( ( count > 0 ) && ( this._count > 0 ) ) {
				lock ( this._synclock )
					Array.Copy(this._items, srcIndex, dstArray, dstIndex, Math.Min(count, this._count));
			}
		}

		public bool Exists ( Predicate<T> predicate ) {
			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = 0 ; i < this._count ; i++ ) {
						if ( predicate(this._items [ i ]) )
							return true;
					}
				}
			}

			return false;
		}

		public T Find ( Predicate<T> predicate ) {
			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = 0 ; i < this._count ; i++ ) {
						if ( predicate(this._items [ i ]) )
							return this._items [ i ];
					}
				}
			}

			return default(T);
		}

		public T FindLast ( Predicate<T> predicate ) {
			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = ( this._count - 1 ) ; i >= 0 ; i-- ) {
						if ( predicate(this._items [ i ]) )
							return this._items [ i ];
					}
				}
			}

			return default(T);
		}

		public List<T> FindAll ( Predicate<T> predicate ) {
			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			List<T> list = new List<T>();

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = 0 ; i < this._count ; i++ ) {
						if ( predicate(this._items [ i ]) )
							list.Add(this._items [ i ]);
					}
				}
			}

			return list;
		}

		public int FindIndex ( Predicate<T> predicate ) {
			return this.FindIndex(0, predicate);
		}
		public int FindIndex ( int offset, Predicate<T> predicate ) {
			return this.FindIndex(offset, ( this._count - offset ), predicate);
		}
		public int FindIndex ( int offset, int count, Predicate<T> predicate ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					return Array.FindIndex<T>(this._items, offset, count, predicate);
			}

			return ( -1 );
		}

		public int FindLastIndex ( Predicate<T> predicate ) {
			return this.FindLastIndex(0, predicate);
		}
		public int FindLastIndex ( int offset, Predicate<T> predicate ) {
			return this.FindLastIndex(offset, ( this._count - offset ), predicate);
		}
		public int FindLastIndex ( int offset, int count, Predicate<T> predicate ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					return Array.FindLastIndex<T>(this._items, offset, count, predicate);
			}

			return ( -1 );
		}

		public void ForEach ( Action<T> action ) {
			if ( action == null )
				throw new ArgumentNullException("action");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = 0 ; i < this._count ; i++ )
						action(this._items [ i ]);
				}
			}
		}

		public List<T> GetRange ( int offset, int count ) {
			if ( ( offset < 0 ) || ( offset >= this.Count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this.Count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			List<T> list = new List<T>(count);

			lock ( this._synclock ) {
				for ( int i = offset, limit = ( offset + count ) ; i < limit ; i++ )
					list.Add(this._items [ i ]);
			}

			return list;
		}

		public void Insert ( int index, T item ) {
			if ( ( index < 0 ) || ( index >= this._count ) )
				throw new ArgumentOutOfRangeException("index");

			lock ( this._synclock ) {
				if ( ( this._count + 1 ) > this._items.Length )
					Array.Resize<T>(ref this._items, ( ( this._items.Length << 1 ) | 1 ));

				Array.Copy(this._items, index, this._items, ( index + 1 ), ( this._count - index ));

				this._items [ index ] = item;
				this._count++;
			}
		}

		public void InsertRange ( int index, IEnumerable<T> collection ) {
			if ( collection == null )
				throw new ArgumentNullException("collection");

			int offset = 0;

			foreach ( T item in collection )
				this.Insert(( index + offset++ ), item);
		}

		public bool Remove ( T item ) {
			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					int index = ( -1 );

					if ( ( index = this.IndexOf(item) ) >= 0 ) {
						this.RemoveAt(index);
						return true;
					}
				}
			}

			return false;
		}

		public void RemoveAll ( Predicate<T> predicate ) {
			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = 0 ; i < this._count ; i++ ) {
						if ( predicate(this._items [ i ]) )
							this.RemoveAt(i);
					}
				}
			}
		}

		public void RemoveAt ( int index ) {
			if ( ( index < 0 ) || ( index >= this._count ) )
				throw new ArgumentOutOfRangeException("index");

			lock ( this._synclock ) {
				for ( int i = index ; i < this._count ; i++ ) {
					if ( ( i + 1 ) < this._count ) {
						this._items [ i ] = this._items [ i + 1 ];
					} else {
						this._items [ i ] = default(T);
					}
				}

				this._count--;
			}
		}

		public void RemoveRange ( int offset, int count ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			lock ( this._synclock ) {
				for ( int i = offset ; i < this._count ; i++ ) {
					if ( ( i + count ) < this._count ) {
						this._items [ i ] = this._items [ i + count ];
					} else {
						this._items [ i ] = default(T);
					}
				}

				this._count -= count;
			}
		}

		public int IndexOf ( T item ) {
			return this.IndexOf(item);
		}
		public int IndexOf ( T item, int offset ) {
			return this.IndexOf(item, offset, ( this._count - offset ));
		}
		public int IndexOf ( T item, int offset, int count ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			lock ( this._synclock )
				return Array.IndexOf<T>(this._items, item, offset, count);
		}

		public int LastIndexOf ( T item ) {
			return this.LastIndexOf(item);
		}
		public int LastIndexOf ( T item, int offset ) {
			return this.LastIndexOf(item, offset, ( this._count - offset ));
		}
		public int LastIndexOf ( T item, int offset, int count ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			lock ( this._synclock )
				return Array.LastIndexOf<T>(this._items, item, offset, count);
		}

		public void Reverse ( ) {
			this.Reverse(0);
		}
		public void Reverse ( int offset ) {
			this.Reverse(offset, ( this._count - offset ));
		}
		public void Reverse ( int offset, int count ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					Array.Reverse(this._items, offset, count);
			}
		}

		public void Sort ( ) {
			this.Sort(0);
		}
		public void Sort ( int offset ) {
			this.Sort(offset, ( this._count - offset ));
		}
		public void Sort ( int offset, int count ) {
			this.Sort(offset, count, Comparer<T>.Default);
		}
		public void Sort ( Comparison<T> comparison ) {
			if ( comparison == null )
				throw new ArgumentNullException("comparison");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					Array.Sort<T>(this._items, comparison);
			}
		}
		public void Sort ( IComparer<T> comparer ) {
			this.Sort(0, comparer);
		}
		public void Sort ( int offset, IComparer<T> comparer ) {
			this.Sort(offset, ( this._count - offset ), comparer);
		}
		public void Sort ( int offset, int count, IComparer<T> comparer ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( comparer == null )
				throw new ArgumentNullException("comparer");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					Array.Sort<T>(this._items, offset, count, comparer);
			}
		}

		public T [ ] ToArray ( ) {
			lock ( this._synclock ) {
				T [ ] array = new T [ this._count ];

				this.CopyTo(array);
				return array;
			}
		}

		public void TrimExcess ( ) {
			lock ( this._synclock ) {
				if ( this._count != this._items.Length )
					Array.Resize<T>(ref this._items, this._count);
			}
		}

		public bool TrueForAll ( Predicate<T> predicate ) {
			return this.TrueForAll(0, predicate);
		}
		public bool TrueForAll ( int offset, Predicate<T> predicate ) {
			return this.TrueForAll(offset, ( this._count - offset ), predicate);
		}
		public bool TrueForAll ( int offset, int count, Predicate<T> predicate ) {
			if ( ( offset < 0 ) || ( offset >= this._count ) )
				throw new ArgumentOutOfRangeException("offset");

			if ( ( count < 0 ) || ( count > ( this._count - offset ) ) )
				throw new ArgumentOutOfRangeException("count");

			if ( predicate == null )
				throw new ArgumentNullException("predicate");

			if ( this._count > 0 ) {
				lock ( this._synclock ) {
					for ( int i = offset ; i < ( offset + count ) ; i++ ) {
						if ( !predicate(this._items [ i ]) )
							return false;
					}
				}
			}

			return true;
		}

		public IEnumerator<T> GetEnumerator ( ) {
			lock ( this._synclock ) {
				for ( int i = 0 ; i < this._count ; i++ )
					yield return this._items [ i ];
			}
		}

		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
		public virtual void GetObjectData ( SerializationInfo info, StreamingContext context ) {
			if ( info == null )
				throw new ArgumentNullException("info");

			lock ( this._synclock ) {
				T [ ] items = new T [ this._count ];

				Array.Copy(this._items, items, this._count);
				info.AddValue("items", items);
				info.AddValue("count", this._count);
			}
		}

		public virtual void OnDeserialization ( object sender ) {
			lock ( this._synclock ) {
				if ( this._info != null ) {
					this._items = ( ( T [ ] ) this._info.GetValue("items", typeof(T [ ])) );
					this._count = this._info.GetInt32("count");
					this._info = null;
				}
			}
		}

		void ICollection.CopyTo ( Array array, int index ) {
			if ( array == null )
				throw new ArgumentNullException("array");

			if ( ( index < 0 ) || ( index >= array.Length ) )
				throw new ArgumentOutOfRangeException("index");

			if ( this._count > 0 ) {
				lock ( this._synclock )
					Array.ConstrainedCopy(this._items, 0, array, index, Math.Min(( array.Length - index ), this._count));
			}
		}

		IEnumerator IEnumerable.GetEnumerator ( ) {
			return this.GetEnumerator();
		}

		int IList.Add ( object value ) {
			this.Add(( T ) value);
			return ( this._count - 1 );
		}

		bool IList.Contains ( object value ) {
			return this.Contains(( T ) value);
		}

		int IList.IndexOf ( object value ) {
			return this.IndexOf(( T ) value);
		}

		void IList.Insert ( int index, object value ) {
			this.Insert(index, ( ( T ) value ));
		}

		void IList.Remove ( object value ) {
			this.Remove(( T ) value);
		}
	}
}
